import string
import os
import time
import types
import socket
import cPickle
import hashlib
import imp

import jsonpickle

"""
    Copyright (C) 2009,2010  Jorge Alegre Vilches

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.    
"""

class SimpleSaaSException(Exception):
	sOp = ''
	sContainer = ''
	sValue = ''
	ArgsDict = {}
	ErrMsg = ''
	def __init__(self, sOp, **ArgsDict):
		self.sOp = sOp
		self.ArgsDict = ArgsDict
		errMsg = 'SimpleSaaS error in operation ' + str(self.sOp)
		List = self.ArgsDict.keys()
		for sName in List:
			if sName != 'msg':
				if len(str(ArgsDict[sName])) < 15:
					errMsg = errMsg + ', ' + str(sName) + '=' + str(ArgsDict[sName])
				else:
					errMsg = errMsg + ', ' + str(sName) + '=' + '*****'
			else:
				if ArgsDict[sName]:
					errMsg = errMsg + '\r\n***** SERVER ERROR FOLLOWS *****\r\n' + str(ArgsDict[sName]) + ' '
		self.ErrMsg = errMsg
	def __str__(self):
		errMsg = self.ErrMsg
		return str(errMsg)

class Service(object):
	"""Service functionality without cloud."""
	_HostName = ''
	_ClassPath = ''
	_RequestDict = {}
	_PORT = 1995
	def __init__(self, sHostName, sClassPath, *ClassArgsTuple, **ClassArgsDict):
		"""Constructor. Accepts arguments and keywork arguments that will be passed to class constructor in server.
		@param sHostName: String : Hostname
		@param sClassPath: String : Full class path, like string.base64.encoder, or module.package.Class, or module.Class."""
		self._HostName = sHostName
		self._ClassPath = sClassPath
		self._RequestDict[u'ClassPath'] = sClassPath
		self._RequestDict[u'ClassArgsTuple'] = ClassArgsTuple
		self._RequestDict[u'ClassArgsDict'] = ClassArgsDict
	def _connect(self, bResponse=True):
		"""Connect method
		@param bResponse [opt]: Boolean : Weather or not we expect response.
		@return: ReponseDict, container with response."""
		sPid = os.getpid()
		self._RequestDict[u'bResponse'] = bResponse
		#print 'RequestDict => ', self._RequestDict
		sRequestDict = jsonpickle.encode(self._RequestDict)
		#print 'sRequestDict => ', sRequestDict
		#print 'Server Data => ', self._HostName, self._PORT		
		s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		s.connect((self._HostName, self._PORT))
		s.sendall(sRequestDict)
		if bResponse:
			reply = []
			while 1:
				try:
					data = s.recv(16384)
					if not data: break
					reply.append(data)
				except socket.error:
					a = 1
			s.close()
			response = string.join(reply,'')
			if response:
				ResponseDict = jsonpickle.decode(response)
			else:
				ResponseDict = {}
				ResponseDict[u'bResult'] = False
		else:
			s.close()
			ResponseDict = {}
		#print 'ResponseDict->', ResponseDict
		return ResponseDict
	def call(self, sMethod, *ArgsTuple, **ArgsDict):
		"""Calls a method. Accepts arguments and keywork arguments that will be passed to method called in the server.
		@param sMethod : Sting : Method
		@return: result : Any Type : Result coming from server."""
		bResponse = True
		if ArgsDict.has_key(u'bResponse'):
			bResponse = ArgsDict[u'bResponse']
		self._RequestDict[u'Method'] = sMethod
		self._RequestDict[u'ArgsTuple'] = ArgsTuple
		self._RequestDict[u'ArgsDict'] = ArgsDict
		Dict = self._connect(bResponse)
		if bResponse:
			if Dict[u'bResult'] == True:
				result = Dict[u'Result']
			else:
				if Dict.has_key(u'sError'):
					sErrMsg = Dict[u'sError']
					raise SimpleSaaSException('call()', msg=sErrMsg)
		return result

def isSerializable(obj):
	"""Checks if Class is serializable by JSON.
	@param sClassPath: Full path for class, like string.base64.Encoding.
	@return: bCheck : Boolean"""
	bCheck = False
	if (type(obj)== types.StringType or type(obj) == types.UnicodeType):
		if obj.find('.') != -1:
			sClassPath = obj
			Fields = sClassPath.split('.')
			sClassName = Fields[len(Fields)-1]
			sPkg = Fields[len(Fields)-2]
			i = 0
			sPath = None
			for sField in Fields:
				if i != (len(Fields)-2) and i != (len(Fields)-1) and sPath == None:
					TupleInfo = imp.find_module(sField)
					sPath = TupleInfo[1]
				elif i != (len(Fields)-2) and i != (len(Fields)-1) and sPath != None:
					TupleInfo = imp.find_module(sField, [sPath])
				i += 1
			# Tuple with last info module for path
			sPath = TupleInfo[1]
			Tuple = imp.find_module(sPkg, [sPath])
			Pkg = imp.load_module(sPkg, Tuple[0], Tuple[1], Tuple[2])
			try:
				SPkg = jsonpickle.encode(Pkg)
				bCheck = True
			except:
				pass
		else:
			try:
				SObj = jsonpickle.encode(obj)
				bCheck = True
			except:
				pass
	else:
		try:
			SObj = jsonpickle.encode(obj)
			bCheck = True
		except:
			pass
	return bCheck
